1 Breast cancer example

  • part of study https://doi.org/10.1093/jnci/djj052)
  • Histologic grade in breast cancer clinically prognostic. Association of histologic grade on expression of KPNA2 gene that is known to be associated with poor BC prognosis.
  • Population: all current and future breast cancer patients







2 Data Exploration

2.1 Import

library(tidyverse)
gene <- read.table("https://raw.githubusercontent.com/statOmics/SGA2020/data/gse2990BreastcancerOneGene.txt",header=TRUE)
head(gene)
##    sample_name grade node size age     gene
## 28   OXFT_2221     3    1  5.5  76 367.8179
## 29    OXFT_209     3    1  2.5  66 590.3576
## 30   OXFT_1769     1    1  3.5  86 346.6583
## 31    OXFT_928     1    0  1.1  47 118.6996
## 32   OXFT_2093     1    1  2.2  74 519.4489
## 33   OXFT_1770     1    1  1.7  69 258.4455

We will transform the variable grade and node to a factor

gene$grade <- as.factor(gene$grade)
gene$node <- as.factor(gene$node)

2.2 Summary statistics

geneSum <- gene %>%
  group_by(grade) %>%
  summarize(mean = mean(gene),
            sd = sd(gene),
            n=length(gene)
            ) %>%
  mutate(se = sd/sqrt(n))
geneSum
## # A tibble: 2 x 5
##   grade  mean    sd     n    se
##   <fct> <dbl> <dbl> <int> <dbl>
## 1 1      264.  117.    19  26.7
## 2 3      606.  267.    17  64.9

2.3 Visualisation

gene %>%
  ggplot(aes(x=grade,y=gene)) +
  geom_boxplot(outlier.shape=NA) +
  geom_point()

We can also save the plots as objects for later use!

p1 <- gene %>%
  ggplot(aes(x=grade,y=gene)) +
  geom_boxplot(outlier.shape=NA) +
  geom_point()

p2 <- gene %>%
  filter(grade==1) %>%
  ggplot(aes(sample=gene)) +
  geom_qq() +
  geom_qq_line()

p3 <- gene %>%
  filter(grade==1) %>%
  ggplot(aes(sample=gene)) +
  geom_qq() +
  geom_qq_line()

p1

p2

p3

2.4 Research questions

Researchers want to assess the association of the histolical grade on KPNA2 gene expression


2.5 Estimation of effect size and standard error

effectSize <- tibble(
  delta = geneSum$mean[2]- geneSum$mean[1],
  seDelta = geneSum %>%
    pull(se) %>%
    .^2 %>%
    sum %>%
    sqrt
  )
effectSize
## # A tibble: 1 x 2
##   delta seDelta
##   <dbl>   <dbl>
## 1  342.    70.2

3 Statistical Inference

  • Researchers want to assess the association of histological grade on KPNA2 gene expression
  • Inference?


  • Researchers want to assess the association of histological grade on KPNA2 gene expression
  • Inference?
  • testing + CI $ $ Assumptions

  • In general we start from alternative hypothese \(H_A\): we want to show an association

  • Gene expression of grade 1 and grade 3 patients is on average different

  • But, we will assess it by falsifying the opposite:

  • The average KPNA2 gene expression of grade 1 and grade 3 patients is equal


  • How likely is it to observe an equal or more extreme association than the one observed in the sample when the null hypothesis is true?

  • When we make assumptions about the distribution of our test statistic we can quantify this probability: p-value.

  • If the p-value is below a significance threshold \(\alpha\) we reject the null hypothesis

We control the probability on a false positive result at the \(\alpha\)-level (type I error)

  • The p-value will only be calculated correctly if the underlying assumptions hold!
library(gridExtra)
p1

grid.arrange(p2,p3,ncol=2)

t.test(gene~grade,data=gene)
## 
##  Welch Two Sample t-test
## 
## data:  gene by grade
## t = -4.8806, df = 21.352, p-value = 7.61e-05
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -488.1734 -196.6587
## sample estimates:
## mean in group 1 mean in group 3 
##        263.5516        605.9677
effectSize <- effectSize %>%
  mutate(t.stat=delta/seDelta) %>%
  mutate(p.value= pt(-abs(t.stat),21.352)*2)

effectSize
## # A tibble: 1 x 4
##   delta seDelta t.stat   p.value
##   <dbl>   <dbl>  <dbl>     <dbl>
## 1  342.    70.2   4.88 0.0000761
  • Intensities are often not normally distributed and have a mean variance relation
  • Commonly log2-transformed
  • Differences on log scale:

\[ \log_2(B) - \log_2(A) = \log_2 \frac{B}{A} = \log_2 FC_{\frac{B}{A}} \]


3.1 Log transformation

gene <- gene %>%
  mutate(lgene = log2(gene))

p1 <- gene %>%
  ggplot(aes(x=grade,y=lgene)) +
  geom_boxplot(outlier.shape=NA) +
  geom_point()

p2 <- gene %>%
  filter(grade==1) %>%
  ggplot(aes(sample=lgene)) +
  geom_qq() +
  geom_qq_line()

p3 <- gene %>%
  filter(grade==1) %>%
  ggplot(aes(sample=lgene)) +
  geom_qq() +
  geom_qq_line()

p1

grid.arrange(p2,p3,ncol=2)

logtest <- t.test(lgene~grade,data=gene)
logtest
## 
##  Welch Two Sample t-test
## 
## data:  lgene by grade
## t = -6.0508, df = 33.962, p-value = 7.432e-07
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -1.6236927 -0.8072052
## sample estimates:
## mean in group 1 mean in group 3 
##        7.912963        9.128412
log2FC <- logtest$estimate[2]-logtest$estimate[1]
log2FC
## mean in group 3 
##        1.215449
names(log2FC) <- "g3-g1"
2^log2FC
##   g3-g1 
## 2.32213

3.2 Conclusion

There is a extremely significant association of the histological grade on the gene expression in tumor tissue. On average, the gene expression for the grade 3 patients is 2.3221304 times higher than the gene expression in grade 1 patients (95% CI [1.75, 3.08], \(p<<0.001\)).



The patients also differ in the their lymph node status. Hence, we have a two factorial design: grade x lymph node status!!!

Solution??


4 General Linear Model

How can we integrate multiple factors and continuous covariates in linear model.

\[ y_i= \beta_0 + \beta_1 x_{i,1} + \beta_2 x_{i,2} + \beta_{12}x_{i,1}x_{i,2}+\epsilon_i, \] with

  • \(x_{i,1}\) a dummy variable for histological grade: \(x_{i,1}=\begin{cases} 0& \text{grade 1}\\ 1& \text{grade 3} \end{cases}\)
  • \(x_{i,2}\) a dummy variable for : \(x_{i,2}=\begin{cases} 0& \text{lymph nodes were not removed}\\ 1& \text{lymph nodes were removed} \end{cases}\)
  • \(\epsilon_i\)?

4.1 Implementation in R

lm1 <- lm(gene~grade*node,data=gene)
summary(lm1)
## 
## Call:
## lm(formula = gene ~ grade * node, data = gene)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -356.85  -91.98  -31.47   53.00  612.73 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept)    207.60      60.00   3.460  0.00155 ** 
## grade3         434.21      84.85   5.117 1.41e-05 ***
## node1          132.88      92.46   1.437  0.16040    
## grade3:node1  -234.43     136.92  -1.712  0.09655 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 199 on 32 degrees of freedom
## Multiple R-squared:  0.4809, Adjusted R-squared:  0.4322 
## F-statistic: 9.881 on 3 and 32 DF,  p-value: 9.181e-05

4.2 Assumptions

plot(lm1)


4.3 Breast cancer example

  • Paper: https://doi.org/10.1093/jnci/djj052
  • Histologic grade in breast cancer provides clinically important prognostic information. Two factors have to be concidered: Histologic grade (grade 1 and grade 3) and lymph node status (0 vs 1). The researchers assessed gene expression of the KPNA2 gene a protein-coding gene associated with breast cancer and are mainly interested in the association of histological grade. Note, that the gene variable consists of background corrected normalized intensities obtained with a microarray platform. Upon log-transformation, they are known to be a good proxy for the \(\log\) transformed concentration of gene expression product of the KPNA2 gene.
  • Research questions and translate them towards model parameters (contrasts)?
  • Make an R markdown file to answer the research questions
library(ExploreModelMatrix)
explMx <- VisualizeDesign(gene,designFormula = ~grade*node)
explMx$plotlist
## [[1]]

You can also explore the model matrix interactively:

ExploreModelMatrix(gene,designFormula = ~grade*node)

5 Linear regression in matrix form

5.1 Scalar form

  • Consider a vector of predictors \(\mathbf{x}=(x_1,\ldots,x_p)\) and
  • a real-valued response \(Y\)
  • then the linear regression model can be written as \[ Y=f(\mathbf{x}) +\epsilon=\beta_0+\sum\limits_{j=1}^p x_j\beta_j + \epsilon \] with i.i.d. \(\epsilon\sim N(0,\sigma^2)\)

5.2 Matrix form

  • \(n\) observations \((\mathbf{x}_1,y_1) \ldots (\mathbf{x}_n,y_n)\)
  • Regression in matrix notation \[\mathbf{Y}=\mathbf{X\beta} + \mathbf{\epsilon}\] with \(\mathbf{Y}=\left[\begin{array}{c}y_1\\ \vdots\\y_n\end{array}\right]\), \(\mathbf{X}=\left[\begin{array}{cccc} 1&x_{11}&\ldots&x_{1p}\\ \vdots&\vdots&&\vdots\\ 1&x_{n1}&\ldots&x_{np} \end{array}\right]\), \(\mathbf{\beta}=\left[\begin{array}{c}\beta_0\\ \vdots\\ \beta_p\end{array}\right]\) and \(\mathbf{\epsilon}=\left[\begin{array}{c} \epsilon_1 \\ \vdots \\ \epsilon_n\end{array}\right]\)

5.3 Least Squares (LS)

  • Minimize the residual sum of squares \[\begin{eqnarray*} RSS(\mathbf{\beta})&=&\sum\limits_{i=1}^n e^2_i\\ &=&\sum\limits_{i=1}^n \left(y_i-\beta_0-\sum\limits_{j=1}^p x_{ij}\beta_j\right)^2 \end{eqnarray*}\]
  • or in matrix notation \[\begin{eqnarray*} RSS(\mathbf{\beta})&=&(\mathbf{Y}-\mathbf{X\beta})^T(\mathbf{Y}-\mathbf{X\beta})\\ &=&\Vert \mathbf{Y}-\mathbf{X\beta}\Vert^2 \end{eqnarray*}\] with the \(L_2\)-norm of a \(p\)-dim. vector \(v\) \(\Vert \mathbf{v} \Vert=\sqrt{v_1^2+\ldots+v_p^2}\) \(\rightarrow\)

5.3.1 Minimize RSS

\[ \begin{array}{ccc} \frac{\partial RSS}{\partial \mathbf{\beta}}&=&\mathbf{0}\\\\ \frac{(\mathbf{Y}-\mathbf{X\beta})^T(\mathbf{Y}-\mathbf{X\beta})}{\partial \mathbf{\beta}}&=&\mathbf{0}\\\\ -2\mathbf{X}^T(\mathbf{Y}-\mathbf{X\beta})&=&\mathbf{0}\\\\ \mathbf{X}^T\mathbf{X\beta}&=&\mathbf{X}^T\mathbf{Y}\\\\ \hat{\mathbf{\beta}}&=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{Y} \end{array} \]


5.4 Variance Estimator?

\[ \begin{array}{ccl} \hat{\boldmath{\Sigma}}_{\hat{\mathbf{\beta}}} &=&\text{var}\left[(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{Y}\right]\\\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\text{var}\left[\mathbf{Y}\right]\mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1}\\\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T(\mathbf{I}\sigma^2)\mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1} \\\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{I}\quad\mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1}\sigma^2\\\\ %\hat{\boldmath{\Sigma}}_{\hat{\mathbf{\beta}}}&=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\var\left[\mathbf{Y}\right](\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^T\mathbf{X}(\mathbf{X}^T\mathbf{X})^{-1}\sigma^2\\\\ &=&(\mathbf{X}^T\mathbf{X})^{-1}\sigma^2 \end{array} \]


6 Homework: Adopt the gene analysis on log scale in matrix form!

  1. Study the solution of the exercise to understand the analysis in R https://gtpb.github.io/PSLS20/pages/08-multipleRegression/08-multipleRegression_KPNA2.html

  2. Calculate

  • model parameters and contrasts of interest
  • standard errors, standard errors on contrasts
  • t-test statistics on the model parameters and contrasts of interest
  1. Compare your results with the output of the lm(.) function

6.1 Inspiration

Tip: details on the implementation can be found in the book of Faraway (chapter 2). https://people.bath.ac.uk/jjf23/book/

  • Design matrix
X <- model.matrix(~grade*node,data=gene)
  • Transpose of a matrix: use function t(.)
t(X)
##              1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
## (Intercept)  1 1 1 1 1 1 1 1 1  1  1  1  1  1  1  1  1  1  1  1  1  1  1
## grade3       1 1 0 0 0 0 0 1 0  1  0  1  0  1  1  0  1  0  0  0  0  1  0
## node1        1 1 1 0 1 1 0 0 0  1  1  0  0  0  0  0  0  0  0  0  0  1  1
## grade3:node1 1 1 0 0 0 0 0 0 0  1  0  0  0  0  0  0  0  0  0  0  0  1  0
##              24 25 26 27 28 29 30 31 32 33 34 35 36
## (Intercept)   1  1  1  1  1  1  1  1  1  1  1  1  1
## grade3        1  1  0  1  1  1  1  0  1  0  0  1  0
## node1         0  0  1  0  1  0  0  1  0  0  1  1  0
## grade3:node1  0  0  0  0  1  0  0  0  0  0  0  1  0
## attr(,"assign")
## [1] 0 1 2 3
## attr(,"contrasts")
## attr(,"contrasts")$grade
## [1] "contr.treatment"
## 
## attr(,"contrasts")$node
## [1] "contr.treatment"
  • Matrix product %*% operator
t(X)%*%X
##              (Intercept) grade3 node1 grade3:node1
## (Intercept)           36     17    14            6
## grade3                17     17     6            6
## node1                 14      6    14            6
## grade3:node1           6      6     6            6
  • Degrees of freedom of a model? $$ df = n-p$
summary(lm1)
## 
## Call:
## lm(formula = gene ~ grade * node, data = gene)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -356.85  -91.98  -31.47   53.00  612.73 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept)    207.60      60.00   3.460  0.00155 ** 
## grade3         434.21      84.85   5.117 1.41e-05 ***
## node1          132.88      92.46   1.437  0.16040    
## grade3:node1  -234.43     136.92  -1.712  0.09655 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 199 on 32 degrees of freedom
## Multiple R-squared:  0.4809, Adjusted R-squared:  0.4322 
## F-statistic: 9.881 on 3 and 32 DF,  p-value: 9.181e-05
dfRes <- (nrow(X)-ncol(X))
dfRes
## [1] 32
  • Variance estimator: MSE

\[ \hat \sigma^2 = \sum\limits_{i=1}^n \epsilon_i^2}{n-p} \]

  • Invert matrix: use function solve(.)

  • Diagonal elements of a matrix: use function diag(.)

t(X)%*%X
##              (Intercept) grade3 node1 grade3:node1
## (Intercept)           36     17    14            6
## grade3                17     17     6            6
## node1                 14      6    14            6
## grade3:node1           6      6     6            6
diag(t(X)%*%X)
##  (Intercept)       grade3        node1 grade3:node1 
##           36           17           14            6
LS0tCnRpdGxlOiAiUmVjYXAgZ2VuZXJhbCBsaW5lYXIgbW9kZWwiCmF1dGhvcjogIkxpZXZlbiBDbGVtZW50IgpkYXRlOiAic3RhdE9taWNzLCBHaGVudCBVbml2ZXJzaXR5IChodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8pIgpvdXRwdXQ6CiAgICBodG1sX2RvY3VtZW50OgogICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICAgIHRoZW1lOiBjb3NtbwogICAgICB0b2M6IHRydWUKICAgICAgdG9jX2Zsb2F0OiB0cnVlCiAgICAgIGhpZ2hsaWdodDogdGFuZ28KICAgICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCi0tLQoKCiMgQnJlYXN0IGNhbmNlciBleGFtcGxlCgotIHBhcnQgb2Ygc3R1ZHkgaHR0cHM6Ly9kb2kub3JnLzEwLjEwOTMvam5jaS9kamowNTIpCi0gSGlzdG9sb2dpYyBncmFkZSBpbiBicmVhc3QgY2FuY2VyIGNsaW5pY2FsbHkgcHJvZ25vc3RpYy4KQXNzb2NpYXRpb24gb2YgaGlzdG9sb2dpYyBncmFkZSBvbiBleHByZXNzaW9uIG9mIEtQTkEyIGdlbmUgdGhhdCBpcyBrbm93biB0byBiZSBhc3NvY2lhdGVkIHdpdGggcG9vciBCQyBwcm9nbm9zaXMuCi0gUG9wdWxhdGlvbjogYWxsIGN1cnJlbnQgYW5kIGZ1dHVyZSBicmVhc3QgY2FuY2VyIHBhdGllbnRzCgohW10oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3N0YXRPbWljcy9TR0EyMDIwL2RhdGEvZmlncy9zdGF0R2Vub21pY3NHZW50MjAxNzE4LTEuanBlZykKCi0tLQoKIVtdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zdGF0T21pY3MvU0dBMjAyMC9kYXRhL2ZpZ3Mvc3RhdEdlbm9taWNzR2VudDIwMTcxOC0yLmpwZWcpCgotLS0KCiFbXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc3RhdE9taWNzL1NHQTIwMjAvZGF0YS9maWdzL3N0YXRHZW5vbWljc0dlbnQyMDE3MTgtMy5qcGVnKQoKLS0tCgoKIVtdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zdGF0T21pY3MvU0dBMjAyMC9kYXRhL2ZpZ3Mvc3RhdEdlbm9taWNzR2VudDIwMTcxOC00LmpwZWcpCgotLS0KCgohW10oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3N0YXRPbWljcy9TR0EyMDIwL2RhdGEvZmlncy9zdGF0R2Vub21pY3NHZW50MjAxNzE4LTUuanBlZykKCi0tLQoKCiFbXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc3RhdE9taWNzL1NHQTIwMjAvZGF0YS9maWdzL3N0YXRHZW5vbWljc0dlbnQyMDE3MTgtNi5qcGVnKQoKLS0tCgojIERhdGEgRXhwbG9yYXRpb24KCiMjIEltcG9ydAoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpnZW5lIDwtIHJlYWQudGFibGUoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zdGF0T21pY3MvU0dBMjAyMC9kYXRhL2dzZTI5OTBCcmVhc3RjYW5jZXJPbmVHZW5lLnR4dCIsaGVhZGVyPVRSVUUpCmhlYWQoZ2VuZSkKYGBgCgpXZSB3aWxsIHRyYW5zZm9ybSB0aGUgdmFyaWFibGUgZ3JhZGUgYW5kIG5vZGUgdG8gYSBmYWN0b3IKCmBgYHtyfQpnZW5lJGdyYWRlIDwtIGFzLmZhY3RvcihnZW5lJGdyYWRlKQpnZW5lJG5vZGUgPC0gYXMuZmFjdG9yKGdlbmUkbm9kZSkKYGBgCgojIyBTdW1tYXJ5IHN0YXRpc3RpY3MKCmBgYHtyfQpnZW5lU3VtIDwtIGdlbmUgJT4lCiAgZ3JvdXBfYnkoZ3JhZGUpICU+JQogIHN1bW1hcml6ZShtZWFuID0gbWVhbihnZW5lKSwKICAgICAgICAgICAgc2QgPSBzZChnZW5lKSwKICAgICAgICAgICAgbj1sZW5ndGgoZ2VuZSkKICAgICAgICAgICAgKSAlPiUKICBtdXRhdGUoc2UgPSBzZC9zcXJ0KG4pKQpnZW5lU3VtCmBgYAoKIyMgVmlzdWFsaXNhdGlvbgoKYGBge3J9CmdlbmUgJT4lCiAgZ2dwbG90KGFlcyh4PWdyYWRlLHk9Z2VuZSkpICsKICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZT1OQSkgKwogIGdlb21fcG9pbnQoKQpgYGAKCldlIGNhbiBhbHNvIHNhdmUgdGhlIHBsb3RzIGFzIG9iamVjdHMgZm9yIGxhdGVyIHVzZSEKCmBgYHtyfQpwMSA8LSBnZW5lICU+JQogIGdncGxvdChhZXMoeD1ncmFkZSx5PWdlbmUpKSArCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGU9TkEpICsKICBnZW9tX3BvaW50KCkKCnAyIDwtIGdlbmUgJT4lCiAgZmlsdGVyKGdyYWRlPT0xKSAlPiUKICBnZ3Bsb3QoYWVzKHNhbXBsZT1nZW5lKSkgKwogIGdlb21fcXEoKSArCiAgZ2VvbV9xcV9saW5lKCkKCnAzIDwtIGdlbmUgJT4lCiAgZmlsdGVyKGdyYWRlPT0xKSAlPiUKICBnZ3Bsb3QoYWVzKHNhbXBsZT1nZW5lKSkgKwogIGdlb21fcXEoKSArCiAgZ2VvbV9xcV9saW5lKCkKCnAxCnAyCnAzCmBgYAoKCiMjIFJlc2VhcmNoIHF1ZXN0aW9ucwoKUmVzZWFyY2hlcnMgd2FudCB0byBhc3Nlc3MgdGhlIGFzc29jaWF0aW9uIG9mIHRoZSBoaXN0b2xpY2FsIGdyYWRlIG9uIEtQTkEyIGdlbmUgZXhwcmVzc2lvbgoKCgoKIVtdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zdGF0T21pY3MvU0dBMjAyMC9kYXRhL2ZpZ3Mvc3RhdEdlbm9taWNzR2VudDIwMTcxOC02LmpwZWcpCgotLS0KCiMjIEVzdGltYXRpb24gb2YgZWZmZWN0IHNpemUgYW5kIHN0YW5kYXJkIGVycm9yCgpgYGB7cn0KZWZmZWN0U2l6ZSA8LSB0aWJibGUoCiAgZGVsdGEgPSBnZW5lU3VtJG1lYW5bMl0tIGdlbmVTdW0kbWVhblsxXSwKICBzZURlbHRhID0gZ2VuZVN1bSAlPiUKICAgIHB1bGwoc2UpICU+JQogICAgLl4yICU+JQogICAgc3VtICU+JQogICAgc3FydAogICkKZWZmZWN0U2l6ZQpgYGAKCiMgU3RhdGlzdGljYWwgSW5mZXJlbmNlCgotIFJlc2VhcmNoZXJzIHdhbnQgdG8gYXNzZXNzIHRoZSBhc3NvY2lhdGlvbiBvZiBoaXN0b2xvZ2ljYWwgZ3JhZGUgb24gS1BOQTIgZ2VuZSBleHByZXNzaW9uCi0gSW5mZXJlbmNlPwoKLS0tCgohW10oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3N0YXRPbWljcy9TR0EyMDIwL2RhdGEvZmlncy9zdGF0R2Vub21pY3NHZW50MjAxNzE4LTcuanBlZykKCi0tLQoKCi0gUmVzZWFyY2hlcnMgd2FudCB0byBhc3Nlc3MgdGhlIGFzc29jaWF0aW9uIG9mIGhpc3RvbG9naWNhbCBncmFkZSBvbiBLUE5BMiBnZW5lIGV4cHJlc3Npb24KLSBJbmZlcmVuY2U/Ci0gdGVzdGluZyArIENJICQgXHJpZ2h0YXJyb3cgJCBBc3N1bXB0aW9ucwoKLS0tCgotIEluIGdlbmVyYWwgd2Ugc3RhcnQgZnJvbSAqKmFsdGVybmF0aXZlIGh5cG90aGVzZSoqICRIX0EkOiB3ZSB3YW50IHRvIHNob3cgYW4gYXNzb2NpYXRpb24KLSBHZW5lIGV4cHJlc3Npb24gb2YgZ3JhZGUgMSBhbmQgZ3JhZGUgMyBwYXRpZW50cyBpcyBvbiBhdmVyYWdlIGRpZmZlcmVudAoKLSBCdXQsIHdlIHdpbGwgYXNzZXNzIGl0IGJ5IGZhbHNpZnlpbmcgdGhlIG9wcG9zaXRlOgoKLSBUaGUgYXZlcmFnZSBLUE5BMiBnZW5lIGV4cHJlc3Npb24gb2YgIGdyYWRlIDEgYW5kIGdyYWRlIDMgcGF0aWVudHMgaXMgZXF1YWwKCi0tLQoKLSBIb3cgbGlrZWx5IGlzIGl0IHRvIG9ic2VydmUgYW4gZXF1YWwgb3IgbW9yZSBleHRyZW1lIGFzc29jaWF0aW9uIHRoYW4gdGhlIG9uZSBvYnNlcnZlZCBpbiB0aGUgc2FtcGxlIHdoZW4gdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0cnVlPwoKLSBXaGVuIHdlIG1ha2UgYXNzdW1wdGlvbnMgYWJvdXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBvdXIgdGVzdCBzdGF0aXN0aWMgd2UgY2FuIHF1YW50aWZ5IHRoaXMgcHJvYmFiaWxpdHk6ICoqcC12YWx1ZSoqLgotIElmIHRoZSBwLXZhbHVlIGlzIGJlbG93IGEgc2lnbmlmaWNhbmNlIHRocmVzaG9sZCAkXGFscGhhJCB3ZSByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcwoKKldlIGNvbnRyb2wgdGhlIHByb2JhYmlsaXR5IG9uIGEgZmFsc2UgcG9zaXRpdmUgcmVzdWx0IGF0IHRoZSAkXGFscGhhJC1sZXZlbCAodHlwZSBJIGVycm9yKSoKCi0gVGhlIHAtdmFsdWUgd2lsbCBvbmx5IGJlIGNhbGN1bGF0ZWQgY29ycmVjdGx5IGlmIHRoZSB1bmRlcmx5aW5nIGFzc3VtcHRpb25zIGhvbGQhCgpgYGB7cn0KbGlicmFyeShncmlkRXh0cmEpCnAxCmdyaWQuYXJyYW5nZShwMixwMyxuY29sPTIpCmBgYAoKYGBge3J9CnQudGVzdChnZW5lfmdyYWRlLGRhdGE9Z2VuZSkKCmVmZmVjdFNpemUgPC0gZWZmZWN0U2l6ZSAlPiUKICBtdXRhdGUodC5zdGF0PWRlbHRhL3NlRGVsdGEpICU+JQogIG11dGF0ZShwLnZhbHVlPSBwdCgtYWJzKHQuc3RhdCksMjEuMzUyKSoyKQoKZWZmZWN0U2l6ZQpgYGAKCi0gSW50ZW5zaXRpZXMgYXJlIG9mdGVuIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZCBhbmQgaGF2ZSBhIG1lYW4gdmFyaWFuY2UgcmVsYXRpb24KLSBDb21tb25seSBsb2cyLXRyYW5zZm9ybWVkCi0gRGlmZmVyZW5jZXMgb24gbG9nIHNjYWxlOgoKJCQKXGxvZ18yKEIpIC0gXGxvZ18yKEEpID0gXGxvZ18yIFxmcmFje0J9e0F9ID0gXGxvZ18yIEZDX3tcZnJhY3tCfXtBfX0KJCQKCgoKIVtdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zdGF0T21pY3MvU0dBMjAyMC9kYXRhL2ZpZ3Mvc3RhdEdlbm9taWNzR2VudDIwMTcxOC04LmpwZWcpCgotLS0KCiMjIExvZyB0cmFuc2Zvcm1hdGlvbgoKYGBge3J9CmdlbmUgPC0gZ2VuZSAlPiUKICBtdXRhdGUobGdlbmUgPSBsb2cyKGdlbmUpKQoKcDEgPC0gZ2VuZSAlPiUKICBnZ3Bsb3QoYWVzKHg9Z3JhZGUseT1sZ2VuZSkpICsKICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZT1OQSkgKwogIGdlb21fcG9pbnQoKQoKcDIgPC0gZ2VuZSAlPiUKICBmaWx0ZXIoZ3JhZGU9PTEpICU+JQogIGdncGxvdChhZXMoc2FtcGxlPWxnZW5lKSkgKwogIGdlb21fcXEoKSArCiAgZ2VvbV9xcV9saW5lKCkKCnAzIDwtIGdlbmUgJT4lCiAgZmlsdGVyKGdyYWRlPT0xKSAlPiUKICBnZ3Bsb3QoYWVzKHNhbXBsZT1sZ2VuZSkpICsKICBnZW9tX3FxKCkgKwogIGdlb21fcXFfbGluZSgpCgpwMQpncmlkLmFycmFuZ2UocDIscDMsbmNvbD0yKQoKbG9ndGVzdCA8LSB0LnRlc3QobGdlbmV+Z3JhZGUsZGF0YT1nZW5lKQpsb2d0ZXN0Cgpsb2cyRkMgPC0gbG9ndGVzdCRlc3RpbWF0ZVsyXS1sb2d0ZXN0JGVzdGltYXRlWzFdCmxvZzJGQwpuYW1lcyhsb2cyRkMpIDwtICJnMy1nMSIKMl5sb2cyRkMKYGBgCgojIyBDb25jbHVzaW9uCgpUaGVyZSBpcyBhIGV4dHJlbWVseSBzaWduaWZpY2FudCBhc3NvY2lhdGlvbiBvZiB0aGUgaGlzdG9sb2dpY2FsIGdyYWRlIG9uIHRoZSBnZW5lIGV4cHJlc3Npb24gaW4gdHVtb3IgdGlzc3VlLiAgT24gYXZlcmFnZSwgdGhlIGdlbmUgZXhwcmVzc2lvbiBmb3IgdGhlIGdyYWRlIDMgcGF0aWVudHMgaXMgYHIgMl5sb2cyRkNgIHRpbWVzIGhpZ2hlciB0aGFuIHRoZSBnZW5lIGV4cHJlc3Npb24gaW4gZ3JhZGUgMSBwYXRpZW50cyAoOTVcJSBDSSAgW2ByIHBhc3RlKHJvdW5kKDJeLWxvZ3Rlc3QkY29uZi5pbnRbMjoxXSwyKSxjb2xsYXBzZT0iLCAiKWBdLCAkcDw8MC4wMDEkKS4KCgoKCiFbXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc3RhdE9taWNzL1NHQTIwMjAvZGF0YS9maWdzL3N0YXRHZW5vbWljc0dlbnQyMDE3MTgtMTAuanBlZykKCi0tLQoKCiFbXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc3RhdE9taWNzL1NHQTIwMjAvZGF0YS9maWdzL3N0YXRHZW5vbWljc0dlbnQyMDE3MTgtMTEuanBlZykKCi0tLQoKVGhlIHBhdGllbnRzIGFsc28gZGlmZmVyIGluIHRoZSB0aGVpciBseW1waCBub2RlIHN0YXR1cy4gSGVuY2UsIHdlIGhhdmUgYSB0d28gZmFjdG9yaWFsIGRlc2lnbjogZ3JhZGUgeCBseW1waCBub2RlIHN0YXR1cyEhIQoKU29sdXRpb24/PwoKIVtdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zdGF0T21pY3MvU0dBMjAyMC9kYXRhL2ZpZ3Mvc3RhdEdlbm9taWNzR2VudDIwMTcxOC0xMi5qcGVnKQoKLS0tCgojIEdlbmVyYWwgTGluZWFyIE1vZGVsCgpIb3cgY2FuIHdlIGludGVncmF0ZSBtdWx0aXBsZSBmYWN0b3JzIGFuZCBjb250aW51b3VzIGNvdmFyaWF0ZXMgaW4gbGluZWFyIG1vZGVsLgoKXFsKeV9pPSBcYmV0YV8wICsgXGJldGFfMSB4X3tpLDF9ICsgXGJldGFfMiB4X3tpLDJ9ICsgXGJldGFfezEyfXhfe2ksMX14X3tpLDJ9K1xlcHNpbG9uX2ksClxdCndpdGgKCi0gJHhfe2ksMX0kIGEgZHVtbXkgdmFyaWFibGUgZm9yIGhpc3RvbG9naWNhbCBncmFkZTogJHhfe2ksMX09XGJlZ2lue2Nhc2VzfQowJiBcdGV4dHtncmFkZSAxfVxcCjEmIFx0ZXh0e2dyYWRlIDN9ClxlbmR7Y2FzZXN9JAotICR4X3tpLDJ9JCBhIGR1bW15IHZhcmlhYmxlIGZvciA6ICR4X3tpLDJ9PVxiZWdpbntjYXNlc30KMCYgXHRleHR7bHltcGggbm9kZXMgd2VyZSBub3QgcmVtb3ZlZH1cXAoxJiBcdGV4dHtseW1waCBub2RlcyB3ZXJlIHJlbW92ZWR9ClxlbmR7Y2FzZXN9JAotICRcZXBzaWxvbl9pJD8KCi0tLQoKIyMgSW1wbGVtZW50YXRpb24gaW4gUgoKYGBge3J9CmxtMSA8LSBsbShnZW5lfmdyYWRlKm5vZGUsZGF0YT1nZW5lKQpzdW1tYXJ5KGxtMSkKYGBgCgotLS0KCiMjIEFzc3VtcHRpb25zCgpgYGB7cn0KcGxvdChsbTEpCmBgYAoKLS0tCgojIyBCcmVhc3QgY2FuY2VyIGV4YW1wbGUKCi0gIFBhcGVyOiBodHRwczovL2RvaS5vcmcvMTAuMTA5My9qbmNpL2RqajA1MgotIEhpc3RvbG9naWMgZ3JhZGUgaW4gYnJlYXN0IGNhbmNlciBwcm92aWRlcyBjbGluaWNhbGx5IGltcG9ydGFudCBwcm9nbm9zdGljIGluZm9ybWF0aW9uLiBUd28gZmFjdG9ycyBoYXZlIHRvIGJlIGNvbmNpZGVyZWQ6IEhpc3RvbG9naWMgZ3JhZGUgKGdyYWRlIDEgYW5kIGdyYWRlIDMpIGFuZCBseW1waCBub2RlIHN0YXR1cyAoMCB2cyAxKS4gVGhlIHJlc2VhcmNoZXJzIGFzc2Vzc2VkIGdlbmUgZXhwcmVzc2lvbiBvZiB0aGUgS1BOQTIgZ2VuZSBhIHByb3RlaW4tY29kaW5nIGdlbmUgYXNzb2NpYXRlZCB3aXRoIGJyZWFzdCBjYW5jZXIgYW5kIGFyZSBtYWlubHkgaW50ZXJlc3RlZCBpbiB0aGUgYXNzb2NpYXRpb24gb2YgaGlzdG9sb2dpY2FsIGdyYWRlLiBOb3RlLCB0aGF0IHRoZSBnZW5lIHZhcmlhYmxlIGNvbnNpc3RzIG9mIGJhY2tncm91bmQgY29ycmVjdGVkIG5vcm1hbGl6ZWQgaW50ZW5zaXRpZXMgb2J0YWluZWQgd2l0aCBhIG1pY3JvYXJyYXkgcGxhdGZvcm0uIFVwb24gbG9nLXRyYW5zZm9ybWF0aW9uLCB0aGV5IGFyZSBrbm93biB0byBiZSBhIGdvb2QgcHJveHkgZm9yIHRoZSAkXGxvZyQgdHJhbnNmb3JtZWQgY29uY2VudHJhdGlvbiBvZiBnZW5lIGV4cHJlc3Npb24gcHJvZHVjdCBvZiB0aGUgS1BOQTIgZ2VuZS4KLSBSZXNlYXJjaCBxdWVzdGlvbnMgYW5kIHRyYW5zbGF0ZSB0aGVtIHRvd2FyZHMgbW9kZWwgcGFyYW1ldGVycyAoY29udHJhc3RzKT8KLSBNYWtlIGFuIFIgbWFya2Rvd24gZmlsZSB0byBhbnN3ZXIgdGhlIHJlc2VhcmNoIHF1ZXN0aW9ucwoKCmBgYHtyfQpsaWJyYXJ5KEV4cGxvcmVNb2RlbE1hdHJpeCkKZXhwbE14IDwtIFZpc3VhbGl6ZURlc2lnbihnZW5lLGRlc2lnbkZvcm11bGEgPSB+Z3JhZGUqbm9kZSkKZXhwbE14JHBsb3RsaXN0CmBgYAoKWW91IGNhbiBhbHNvIGV4cGxvcmUgdGhlIG1vZGVsIG1hdHJpeCBpbnRlcmFjdGl2ZWx5OgoKYGBge3IgZXZhbD1GQUxTRX0KRXhwbG9yZU1vZGVsTWF0cml4KGdlbmUsZGVzaWduRm9ybXVsYSA9IH5ncmFkZSpub2RlKQpgYGAKLS0tCgojIExpbmVhciByZWdyZXNzaW9uIGluIG1hdHJpeCBmb3JtCgojIyBTY2FsYXIgZm9ybQoKLSBDb25zaWRlciBhIHZlY3RvciBvZiBwcmVkaWN0b3JzICRcbWF0aGJme3h9PSh4XzEsXGxkb3RzLHhfcCkkIGFuZAotIGEgcmVhbC12YWx1ZWQgcmVzcG9uc2UgJFkkCi0gdGhlbiB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgY2FuIGJlIHdyaXR0ZW4gYXMKXFsKWT1mKFxtYXRoYmZ7eH0pICtcZXBzaWxvbj1cYmV0YV8wK1xzdW1cbGltaXRzX3tqPTF9XnAgeF9qXGJldGFfaiArIFxlcHNpbG9uClxdCndpdGggaS5pLmQuICRcZXBzaWxvblxzaW0gTigwLFxzaWdtYV4yKSQKCiMjIE1hdHJpeCBmb3JtCgotICRuJCBvYnNlcnZhdGlvbnMgJChcbWF0aGJme3h9XzEseV8xKSBcbGRvdHMgKFxtYXRoYmZ7eH1fbix5X24pJAotIFJlZ3Jlc3Npb24gaW4gbWF0cml4IG5vdGF0aW9uClxbXG1hdGhiZntZfT1cbWF0aGJme1hcYmV0YX0gKyBcbWF0aGJme1xlcHNpbG9ufVxdCndpdGggJFxtYXRoYmZ7WX09XGxlZnRbXGJlZ2lue2FycmF5fXtjfXlfMVxcIFx2ZG90c1xceV9uXGVuZHthcnJheX1ccmlnaHRdJCwKJFxtYXRoYmZ7WH09XGxlZnRbXGJlZ2lue2FycmF5fXtjY2NjfSAxJnhfezExfSZcbGRvdHMmeF97MXB9XFwKXHZkb3RzJlx2ZG90cyYmXHZkb3RzXFwKMSZ4X3tuMX0mXGxkb3RzJnhfe25wfQpcZW5ke2FycmF5fVxyaWdodF0kLAokXG1hdGhiZntcYmV0YX09XGxlZnRbXGJlZ2lue2FycmF5fXtjfVxiZXRhXzBcXCBcdmRvdHNcXCBcYmV0YV9wXGVuZHthcnJheX1ccmlnaHRdJCBhbmQKJFxtYXRoYmZ7XGVwc2lsb259PVxsZWZ0W1xiZWdpbnthcnJheX17Y30gXGVwc2lsb25fMSBcXCBcdmRvdHMgXFwgXGVwc2lsb25fblxlbmR7YXJyYXl9XHJpZ2h0XSQKCiMjIExlYXN0IFNxdWFyZXMgKExTKQotIE1pbmltaXplIHRoZSByZXNpZHVhbCBzdW0gb2Ygc3F1YXJlcwpcYmVnaW57ZXFuYXJyYXkqfQpSU1MoXG1hdGhiZntcYmV0YX0pJj0mXHN1bVxsaW1pdHNfe2k9MX1ebiBlXjJfaVxcCiY9JlxzdW1cbGltaXRzX3tpPTF9Xm4gXGxlZnQoeV9pLVxiZXRhXzAtXHN1bVxsaW1pdHNfe2o9MX1ecCB4X3tpan1cYmV0YV9qXHJpZ2h0KV4yClxlbmR7ZXFuYXJyYXkqfQotIG9yIGluIG1hdHJpeCBub3RhdGlvbgpcYmVnaW57ZXFuYXJyYXkqfQpSU1MoXG1hdGhiZntcYmV0YX0pJj0mKFxtYXRoYmZ7WX0tXG1hdGhiZntYXGJldGF9KV5UKFxtYXRoYmZ7WX0tXG1hdGhiZntYXGJldGF9KVxcCiY9JlxWZXJ0IFxtYXRoYmZ7WX0tXG1hdGhiZntYXGJldGF9XFZlcnReMgpcZW5ke2VxbmFycmF5Kn0Kd2l0aCB0aGUgJExfMiQtbm9ybSBvZiBhICRwJC1kaW0uIHZlY3RvciAkdiQgJFxWZXJ0IFxtYXRoYmZ7dn0gXFZlcnQ9XHNxcnR7dl8xXjIrXGxkb3RzK3ZfcF4yfSQKJFxyaWdodGFycm93JCBcYWxlcnR7JFxoYXR7XG1hdGhiZntcYmV0YX19PVx0ZXh0e2FyZ21pbn1fXGJldGEgXFZlcnQgXG1hdGhiZntZfS1cbWF0aGJme1hcYmV0YX1cVmVydF4yJH0KCi0tLQoKIyMjIE1pbmltaXplIFJTUwpcWwpcYmVnaW57YXJyYXl9e2NjY30KXGZyYWN7XHBhcnRpYWwgUlNTfXtccGFydGlhbCBcbWF0aGJme1xiZXRhfX0mPSZcbWF0aGJmezB9XFxcXApcZnJhY3soXG1hdGhiZntZfS1cbWF0aGJme1hcYmV0YX0pXlQoXG1hdGhiZntZfS1cbWF0aGJme1hcYmV0YX0pfXtccGFydGlhbCBcbWF0aGJme1xiZXRhfX0mPSZcbWF0aGJmezB9XFxcXAotMlxtYXRoYmZ7WH1eVChcbWF0aGJme1l9LVxtYXRoYmZ7WFxiZXRhfSkmPSZcbWF0aGJmezB9XFxcXApcbWF0aGJme1h9XlRcbWF0aGJme1hcYmV0YX0mPSZcbWF0aGJme1h9XlRcbWF0aGJme1l9XFxcXApcaGF0e1xtYXRoYmZ7XGJldGF9fSY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UXG1hdGhiZntZfQpcZW5ke2FycmF5fQpcXQoKLS0tCgojIyBWYXJpYW5jZSBFc3RpbWF0b3I/ClxbClxiZWdpbnthcnJheX17Y2NsfQpcaGF0e1xib2xkbWF0aHtcU2lnbWF9fV97XGhhdHtcbWF0aGJme1xiZXRhfX19CiY9Jlx0ZXh0e3Zhcn1cbGVmdFsoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVFxtYXRoYmZ7WX1ccmlnaHRdXFxcXAomPSYoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVFx0ZXh0e3Zhcn1cbGVmdFtcbWF0aGJme1l9XHJpZ2h0XVxtYXRoYmZ7WH0oXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxcXFwKJj0mKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cbWF0aGJme1h9XlQoXG1hdGhiZntJfVxzaWdtYV4yKVxtYXRoYmZ7WH0oXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfQpcXFxcCiY9JihcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XG1hdGhiZntYfV5UXG1hdGhiZntJfVxxdWFkXG1hdGhiZntYfShcbWF0aGJme1h9XlRcbWF0aGJme1h9KV57LTF9XHNpZ21hXjJcXFxcCiVcaGF0e1xib2xkbWF0aHtcU2lnbWF9fV97XGhhdHtcbWF0aGJme1xiZXRhfX19Jj0mKFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0pXnstMX1cbWF0aGJme1h9XlRcdmFyXGxlZnRbXG1hdGhiZntZfVxyaWdodF0oXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1cXAomPSYoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0oXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxzaWdtYV4yXFxcXAomPSYoXG1hdGhiZntYfV5UXG1hdGhiZntYfSleey0xfVxzaWdtYV4yClxlbmR7YXJyYXl9ClxdCgotLS0KCiMgSG9tZXdvcms6IEFkb3B0IHRoZSBnZW5lIGFuYWx5c2lzIG9uIGxvZyBzY2FsZSBpbiBtYXRyaXggZm9ybSEKCjEuIFN0dWR5IHRoZSBzb2x1dGlvbiBvZiB0aGUgZXhlcmNpc2UgdG8gdW5kZXJzdGFuZCB0aGUgYW5hbHlzaXMgaW4gUgpodHRwczovL2d0cGIuZ2l0aHViLmlvL1BTTFMyMC9wYWdlcy8wOC1tdWx0aXBsZVJlZ3Jlc3Npb24vMDgtbXVsdGlwbGVSZWdyZXNzaW9uX0tQTkEyLmh0bWwKCjIuIENhbGN1bGF0ZQotIG1vZGVsIHBhcmFtZXRlcnMgYW5kIGNvbnRyYXN0cyBvZiBpbnRlcmVzdAotIHN0YW5kYXJkIGVycm9ycywgc3RhbmRhcmQgZXJyb3JzIG9uIGNvbnRyYXN0cwotIHQtdGVzdCBzdGF0aXN0aWNzIG9uIHRoZSBtb2RlbCBwYXJhbWV0ZXJzIGFuZCBjb250cmFzdHMgb2YgaW50ZXJlc3QKCjMuIENvbXBhcmUgeW91ciByZXN1bHRzIHdpdGggdGhlIG91dHB1dCBvZiB0aGUgbG0oLikgZnVuY3Rpb24KCgotLS0KCiMjIEluc3BpcmF0aW9uCgpUaXA6IGRldGFpbHMgb24gdGhlIGltcGxlbWVudGF0aW9uIGNhbiBiZSBmb3VuZCBpbiB0aGUgYm9vayBvZiBGYXJhd2F5IChjaGFwdGVyIDIpLiBodHRwczovL3Blb3BsZS5iYXRoLmFjLnVrL2pqZjIzL2Jvb2svCgotIERlc2lnbiBtYXRyaXgKCmBgYHtyfQpYIDwtIG1vZGVsLm1hdHJpeCh+Z3JhZGUqbm9kZSxkYXRhPWdlbmUpCmBgYAoKLSBUcmFuc3Bvc2Ugb2YgYSBtYXRyaXg6IHVzZSBmdW5jdGlvbiB0KC4pCgpgYGB7cn0KdChYKQpgYGAKCi0gTWF0cml4IHByb2R1Y3QgJVwqJSBvcGVyYXRvcgoKYGBge3J9CnQoWCklKiVYCmBgYAoKLSBEZWdyZWVzIG9mIGZyZWVkb20gb2YgYSBtb2RlbD8KJCQgZGYgPSAgbi1wJAoKYGBge3J9CnN1bW1hcnkobG0xKQpkZlJlcyA8LSAobnJvdyhYKS1uY29sKFgpKQpkZlJlcwpgYGAKCi0gVmFyaWFuY2UgZXN0aW1hdG9yOiBNU0UKCiQkClxoYXQgXHNpZ21hXjIgPSBcc3VtXGxpbWl0c197aT0xfV5uIFxlcHNpbG9uX2leMn17bi1wfQokJAoKCi0gSW52ZXJ0IG1hdHJpeDogdXNlIGZ1bmN0aW9uIHNvbHZlKC4pCgotIERpYWdvbmFsIGVsZW1lbnRzIG9mIGEgbWF0cml4OiB1c2UgZnVuY3Rpb24gZGlhZyguKQoKYGBge3J9CnQoWCklKiVYCmRpYWcodChYKSUqJVgpCmBgYAo=